/************************************************************************
 *   IRC - Internet Relay Chat, ircd/s_serv.c (formerly ircd/s_msg.c)
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   See file AUTHORS in IRC package for additional names of
 *   the programmers.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "userload.h"
#if defined(PCS) || defined(AIX) || defined(DYNIXPTX) || defined(SVR3)
#include <time.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <utmp.h>
#include <time.h>
#include "h.h"

static void report_jinx (aClient * sptr);

static char buf[BUFSIZE];
VOIDSIG s_die ();
/* I guess we can move this to somewhere else ... */

/*
** m_functions execute protocol messages on this server:
**
**   cptr    is always NON-NULL, pointing to a *LOCAL* client
**       structure (with an open socket connected!). This
**       identifies the physical socket where the message
**       originated (or which caused the m_function to be
**       executed--some m_functions may call others...).
**
**   sptr    is the source of the message, defined by the
**       prefix part of the message if present. If not
**       or prefix not found, then sptr==cptr.
**
**       (!IsServer(cptr)) => (cptr == sptr), because
**       prefixes are taken *only* from servers...
**
**       (IsServer(cptr))
**           (sptr == cptr) => the message didn't
**           have the prefix.
**
**           (sptr != cptr && IsServer(sptr) means
**           the prefix specified servername. (?)
**
**           (sptr != cptr && !IsServer(sptr) means
**           that message originated from a remote
**           user (not local).
**
**       combining
**
**       (!IsServer(sptr)) means that, sptr can safely
**       taken as defining the target structure of the
**       message in this server.
**
**   *Always* true (if 'parse' and others are working correct):
**
**   1)  sptr->from == cptr  (note: cptr->from == cptr)
**
**   2)  MyConnect(sptr) <=> sptr == cptr (e.g. sptr
**       *cannot* be a local connection, unless it's
**       actually cptr!). [MyConnect(x) should probably
**       be defined as (x == x->from) --msa ]
**
**   parc    number of variable parameter strings (if zero,
**       parv is allowed to be NULL)
**
**   parv    a NULL terminated list of parameter pointers,
**
**           parv[0], sender (prefix string), if not present
**               this points to an empty string.
**           parv[1]...parv[parc-1]
**               pointers to additional parameters
**           parv[parc] == NULL, *always*
**
**       note:   it is guaranteed that parv[0]..parv[parc-1] are all
**           non-NULL pointers.
*/

/*
** m_version
**   parv[0] = sender prefix
**   parv[1] = remote server
*/
int m_version (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    extern char serveropts[];

    /* Make sure they stick to the local server if not registered.
     *  -Studded */
    if ((!IsRegistered (cptr)) && (!BadPtr (parv[1]))) {
	sendto_one (cptr, ":%s %d VERSION :You have not registered",
		    me.name, ERR_NOTREGISTERED);
	return -1;
    }
    if (hunt_server (cptr, sptr, ":%s VERSION :%s", 1, parc, parv) ==
	HUNTED_ISME)
	sendto_one (sptr, rpl_str (RPL_VERSION), me.name, parv[0], version,
		    debugmode, me.name, serveropts);
    return 0;
}

/*
   ** m_squit
   **   parv[0] = sender prefix
   **   parv[1] = server name
   **   parv[parc-1] = comment
 */
int m_squit (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aConfItem *aconf;
    char *server;
    aClient *acptr;
    char *comment = (parc > 2
		     && parv[parc - 1]) ? parv[parc - 1] : cptr->name;

    if (check_registered (sptr))
	return 0;

    if (!IsPrivileged (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    if (parc > 1) {
	server = parv[1];
	/*
	   ** To accomodate host masking, a squit for a masked server
	   ** name is expanded if the incoming mask is the same as
	   ** the server name for that link to the name of link.
	 */
	while ((*server == '*') && IsServer (cptr)) {
	    aconf = cptr->serv->nline;
	    if (!aconf)
		break;
	    if (!mycmp (server, my_name_for_link (me.name, aconf)))
		server = cptr->name;
	    break;		  /* WARNING is normal here */
	}
	/*
	   ** The following allows wild cards in SQUIT. Only usefull
	   ** when the command is issued by an oper.
	 */
	for (acptr = client; (acptr = next_client (acptr, server));
	     acptr = acptr->next)
	    if (IsServer (acptr) || IsMe (acptr))
		break;
	if (acptr && IsMe (acptr)) {
	    acptr = cptr;
	    server = cptr->sockhost;
	}
    }
    else {
	/*
	   ** This is actually protocol error. But, well, closing
	   ** the link is very proper answer to that...
	 */
	server = cptr->sockhost;
	acptr = cptr;
    }

    /*
       ** SQUIT semantics is tricky, be careful...
       **
       ** The old (irc2.2PL1 and earlier) code just cleans away the
       ** server client from the links (because it is never true
       ** "cptr == acptr".
       **
       ** This logic here works the same way until "SQUIT host" hits
       ** the server having the target "host" as local link. Then it
       ** will do a real cleanup spewing SQUIT's and QUIT's to all
       ** directions, also to the link from which the orinal SQUIT
       ** came, generating one unnecessary "SQUIT host" back to that
       ** link.
       **
       ** One may think that this could be implemented like
       ** "hunt_server" (e.g. just pass on "SQUIT" without doing
       ** nothing until the server having the link as local is
       ** reached). Unfortunately this wouldn't work in the real life,
       ** because either target may be unreachable or may not comply
       ** with the request. In either case it would leave target in
       ** links--no command to clear it away. So, it's better just
       ** clean out while going forward, just to be sure.
       **
       ** ...of course, even better cleanout would be to QUIT/SQUIT
       ** dependant users/servers already on the way out, but
       ** currently there is not enough information about remote
       ** clients to do this...   --msa
     */
    if (!acptr) {
	sendto_one (sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0],
		    server);
	return 0;
    }
    if (MyClient (sptr) && ((!OPCanGRoute (sptr) && !MyConnect (acptr)) ||
			    (!OPCanLRoute (sptr) && MyConnect (acptr)))) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    /*
       **  Notify all opers, if my local link is remotely squitted
     */
    if (MyConnect (acptr) && !IsAnOper (cptr)) {
	sendto_locfailops ("Received SQUIT %s from %s (%s)",
			   acptr->name, get_client_name (sptr, FALSE),
			   comment);
	sendto_serv_butone (&me,
			    ":%s GLOBOPS :Received SQUIT %s from %s (%s)",
			    me.name, server, get_client_name (sptr, FALSE),
			    comment);
#if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT)
	syslog (LOG_DEBUG, "SQUIT From %s : %s (%s)", parv[0], server,
		comment);
#endif
    }
    else if (MyConnect (acptr)) {
	sendto_locfailops ("Received SQUIT %s from %s (%s)",
			   acptr->name, get_client_name (sptr, FALSE),
			   comment);
	sendto_serv_butone (&me,
			    ":%s GLOBOPS :Received SQUIT %s from %s (%s)",
			    me.name, acptr->name, get_client_name (sptr,
								   FALSE),
			    comment);
    }
    if (IsAnOper (sptr)) {
	/*
	 * It was manually /squit'ed by a human being(we hope),
	 * there is a very good chance they don't want us to
	 * reconnect right away.  -Cabal95
	 */
	acptr->flags |= FLAGS_SQUIT;
    }
    return exit_client (cptr, acptr, sptr, comment);
}

/*
 * m_protoctl
 *  parv[0] = Sender prefix
 *  parv[1+] = Options
 */
int m_protoctl (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int i;
    char proto[128], *options, *equal;
    static char *dummyblank = "";	/* Yes, it is kind of ugly */


    if (GotProtoctl (sptr)) {
	/*
	 * But we already GOT a protoctl msg!
	 */
	sendto_one (cptr, "ERROR :Already got a PROTOCTL from you.");
	return 0;
    }
    cptr->flags |= FLAGS_PROTOCTL;

    for (i = 1; i < parc; i++) {
	strncpy (proto, parv[i], 127);
	proto[127] = '\0';	  /* Just to be safe... */
	equal = (char *) index (proto, '=');
	if (equal == NULL)
	    options = dummyblank;
	else {
	    options = &equal[1];  /* Variable-byte-size safe */
	    equal[0] = '\0';
	}

	if (strcmp (proto, "NOQUIT") == 0) {
	    SetNoQuit (cptr);
	}
	else if (strcmp (proto, "TOKEN") == 0) {
	    SetToken (cptr);
	}
	/*
	 * Add other protocol extensions here, with proto
	 * containing the base option, and options containing
	 * what it equals, if anything.
	 *
	 * DO NOT error or warn on unknown proto; we just don't
	 * support it.
	 */
    }

    return 0;
}

/*
   ** m_server (changed 05/10/99 to send server-version with it too -GZ)
   **
   **   parv[0] = sender prefix
   **   parv[1] = servername
   **   parv[2] = hopcount
   **   parv[3] = timestamp / version
   **   parv[4] = version
   **   parv[5] = info
 */

int m_server (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *ch;
    int i;
    char info[REALLEN + 1], *inpath, *host, *encr;
    char s_version[REALLEN + 1];
    aClient *acptr, *bcptr;
    aConfItem *aconf, *cconf;
    int hop, ts = 0;

    info[0] = '\0';
    s_version[0] = '\0';
    inpath = get_client_name (cptr, FALSE);
    if (parc < 2 || *parv[1] == '\0') {
	sendto_one (cptr, "ERROR :No servername");
	return 0;
    }
    hop = 0;
    host = parv[1];

    if (parc > 5) {
	ts = atoi (parv[3]);
	hop = atoi (parv[2]);
	(void) strncpy (info, parv[5], REALLEN);
	info[REALLEN] = '\0';
	(void) strncpy (s_version, parv[4], REALLEN);
	s_version[REALLEN] = '\0';
    }
    else if (parc > 4 && atoi (parv[2])) {
	hop = atoi (parv[2]);
	(void) strncpy (info, parv[4], REALLEN);
	info[REALLEN] = '\0';
	(void) strncpy (s_version, parv[3], REALLEN);
	s_version[REALLEN] = '\0';
    }
    else if (parc > 3) {
	(void) strncpy (info, parv[3], REALLEN);
	info[REALLEN] = '\0';
	(void) strncpy (s_version, parv[2], REALLEN);
	s_version[REALLEN] = '\0';
    }

    /*
       ** Check for "FRENCH " infection ;-) (actually this should
       ** be replaced with routine to check the hostname syntax in
       ** general). [ This check is still needed, even after the parse
       ** is fixed, because someone can send "SERVER :foo bar " ].
       ** Also, changed to check other "difficult" characters, now
       ** that parse lets all through... --msa
     */
    if (strlen (host) > HOSTLEN)
	host[HOSTLEN] = '\0';
    for (ch = host; *ch; ch++)
	if (*ch <= ' ' || *ch > '~')
	    break;
    if (*ch || !index (host, '.')) {
	sendto_one (sptr, "ERROR :Bogus server name (%s)", sptr->name, host);
	sendto_ops ("Bogus server name (%s) from %s", host,
		    get_client_name (cptr, TRUE));
	sptr->since += 7;
	return 0;
    }
    if (IsPerson (cptr)) {
	/*
	   ** A local link that has been identified as a USER
	   ** tries something fishy... ;-)
	 */
	sendto_one (cptr, err_str (ERR_ALREADYREGISTRED), me.name, parv[0]);
	sendto_one (cptr, ":%s NOTICE %s :Sorry, but your IRC "
		    "program doesn't appear to support changing "
		    "servers.", me.name, cptr->name);
	sendto_ops ("User %s trying to become a server %s",
		    get_client_name (cptr, TRUE), host);
	sptr->since += 7;
	return 0;
    }
    /* *WHEN* can it be that "cptr != sptr" ????? --msa */
    /* When SERVER command (like now) has prefix. -avalon */

    /* Get a pre-peek at the password... */
    if (IsUnknown (cptr)) {
	aconf = find_conf_servern (host);
	if (!aconf) {
	    sendto_one (cptr, "ERROR :No Access (No N line) %s", inpath);
	    sendto_locfailops ("Access denied (No N line) %s", inpath);
	    return exit_client (cptr, cptr, cptr, "No N line");
	}
	encr = cptr->passwd;
	if (*aconf->passwd && !StrEq (aconf->passwd, encr)) {
	    sendto_one (cptr, "ERROR :No Access (passwd mismatch) %s",
			inpath);
	    sendto_locfailops ("Access denied (passwd mismatch) %s", inpath);
	    return exit_client (cptr, cptr, cptr, "Bad Password");
	}
	/*  bzero(cptr->passwd, sizeof(cptr->passwd)); */
    }
    if ((acptr = find_name (host, NULL))) {
	aClient *ocptr;

	/*
	 * This link is trying feed me a server that I already have
	 * access through another path -- multiple paths not accepted
	 * currently, kill this link immeatedly!!
	 *
	 * Rather than KILL the link which introduced it, KILL the
	 * youngest of the two links. -avalon
	 */
	acptr = acptr->from;
	ocptr = (cptr->firsttime > acptr->firsttime) ? acptr : cptr;
	acptr = (cptr->firsttime > acptr->firsttime) ? cptr : acptr;
	sendto_one (acptr, "ERROR :Server %s already exists from %s",
		    host, (ocptr->from ? ocptr->from->name : "<nobody>"));
	sendto_ops ("Link %s cancelled, server %s already exists from %s",
		    get_client_name (acptr, TRUE), host,
		    (ocptr->from ? ocptr->from->name : "<nobody>"));
	return exit_client (acptr, acptr, acptr, "Server Exists");
    }
    if ((acptr = find_client (host, NULL))) {
	/*
	   ** Server trying to use the same name as a person. Would
	   ** cause a fair bit of confusion. Enough to make it hellish
	   ** for a while and servers to send stuff to the wrong place.
	 */
	sendto_one (cptr, "ERROR :Nickname %s already exists!", host);
	sendto_locfailops ("Link %s cancelled: Server/nick collision on %s",
			   inpath, host);
	sendto_serv_butone (&me,
			    ":%s GLOBOPS : Link %s cancelled: Server/nick collision on %s",
			    parv[0], inpath, host);
	return exit_client (cptr, cptr, cptr, "Nick as Server");
    }
    if (IsServer (cptr)) {
	/*
	   ** Server is informing about a new server behind
	   ** this link. Create REMOTE server structure,
	   ** add it to list and propagate word to my other
	   ** server links...
	 */
	if (parc == 1 || info[0] == '\0') {
	    sendto_one (cptr, "ERROR :No server info specified for %s", host);
	    return 0;
	}
	/*
	   **
	 */
	if (!(aconf = find_conf_host (cptr->confs, host, CONF_HUB)) ||
	    (aconf->port && (hop > aconf->port))) {
	    sendto_ops ("Non-Hub link %s introduced %s(%s).",
			get_client_name (cptr, TRUE), host,
			aconf ? (aconf->host ? aconf->host : "*") : "!");
	    return exit_client (cptr, cptr, cptr, "Too many servers");
	}
	/*
	   ** See if the newly found server has a Q line for it in
	   ** our conf. If it does, lose the link that brought it
	   ** into our network. Format:
	   **
	   ** Q:<unused>:<reason>:<servername>
	   **
	   ** Example:  Q:*:for the hell of it:eris.Berkeley.EDU
	 */
	if ((aconf = find_conf_name (host, CONF_QUARANTINED_SERVER))) {
	    sendto_one (cptr,
			"ERROR :%s is not welcome: %s. %s",
			host, BadPtr (aconf->passwd) ?
			"reason unspecified" : aconf->passwd,
			"Try another network");

	    return exit_client (cptr, cptr, cptr, "Q-Lined Server");
	}
	acptr = make_client (cptr, find_server (parv[0], NULL));
	(void) make_server (acptr);
	acptr->hopcount = hop;

	strncpyzt (acptr->name, host, sizeof (acptr->name));
	strncpyzt (acptr->info, info, sizeof (acptr->info));
	strncpyzt (acptr->version, s_version, REALLEN);
	strncpyzt (acptr->serv->up, parv[0], sizeof (acptr->serv->up));
	SetServer (acptr);
	acptr->flags |= FLAGS_TS8;
	add_client_to_list (acptr);
	(void) add_to_client_hash_table (acptr->name, acptr);
	/*
	   ** Old sendto_serv_but_one() call removed because we now
	   ** need to send different names to different servers
	   ** (domain name matching)
	 */
	for (i = 0; i <= highest_fd; i++) {
	    if (!(bcptr = local[i]) || !IsServer (bcptr) ||
		bcptr == cptr || IsMe (bcptr))
		continue;
	    if (!(aconf = bcptr->serv->nline)) {
		sendto_ops ("Lost N-line for %s on %s. Closing",
			    get_client_name (cptr, TRUE), host);
		return exit_client (cptr, cptr, cptr, "Lost N line");
	    }
	    if (match (my_name_for_link (me.name, aconf), acptr->name) == 0)
		continue;
	    if (ts)
		sendto_one (bcptr, ":%s SERVER %s %d %d %s :%s",
			    parv[0], acptr->name, hop + 1, ts,
			    acptr->version, acptr->info);
	    else
		sendto_one (bcptr, ":%s SERVER %s %d %s :%s",
			    parv[0], acptr->name, hop + 1,
			    acptr->version, acptr->info);
	}
	/* Check for U-line status -- Barubary */
	if (find_conf_host (cptr->confs, acptr->name, CONF_UWORLD))
	    acptr->flags |= FLAGS_ULINE;
#ifdef	USE_SERVICES
	check_services_butone (SERVICE_WANT_SERVER, sptr,
			       ":%s SERVER %s %d %s :%s", parv[0],
			       acptr->name, hop + 1, acptr->version,
			       acptr->info);
#endif
	return 0;
    }
    if (!IsUnknown (cptr) && !IsHandshake (cptr))
	return 0;
    /*
       ** A local link that is still in undefined state wants
       ** to be a SERVER. Check if this is allowed and change
       ** status accordingly...
     */
    strncpyzt (cptr->name, host, sizeof (cptr->name));
    strncpyzt (cptr->info, info[0] ? info : me.name, sizeof (cptr->info));
    strncpyzt (cptr->version, s_version, REALLEN);
    cptr->hopcount = hop;

    switch (check_server_init (cptr)) {
    case 0:
	return m_server_estab (cptr);
    case 1:
	sendto_ops ("Access check for %s in progress",
		    get_client_name (cptr, TRUE));
	return 1;
    default:
	ircstp->is_ref++;
	sendto_ops ("Received unauthorized connection from %s.",
		    get_client_host (cptr));
	sendto_serv_butone (&me,
			    ":%s GLOBOPS :Recieved unauthorized connection from %s.",
			    parv[0], get_client_host (cptr));
	return exit_client (cptr, cptr, cptr, "No C/N conf lines");
    }

}

int m_server_estab (aClient *cptr)
{
    aClient *acptr;
    aConfItem *aconf, *bconf;
    aChannel *chptr;
    aJinxItem *ajinx;
    aSqlineItem *asqline;

    char *inpath, *host, *s, *encr, *ver;
    int split, i, verflags;
    char link_caps[200] = "";

    inpath = get_client_name (cptr, TRUE);	/* "refresh" inpath with host */
    split = mycmp (cptr->name, cptr->sockhost);
    host = cptr->name;

    current_load_data.conn_count++;
    update_load ();

    if (!(aconf = find_conf (cptr->confs, host, CONF_NOCONNECT_SERVER |
			     CONF_NZCONNECT_SERVER))) {
	ircstp->is_ref++;
	sendto_one (cptr,
		    "ERROR :Access denied. No N line for server %s", inpath);
	sendto_ops ("Access denied. No N line for server %s", inpath);
	return exit_client (cptr, cptr, cptr, "No N line for server");
    }
    if (!(bconf = find_conf (cptr->confs, host, CONF_CONNECT_SERVER |
			     CONF_NZCONNECT_SERVER))) {
	ircstp->is_ref++;
	sendto_one (cptr, "ERROR :Only N (no C) field for server %s", inpath);
	sendto_ops ("Only N (no C) field for server %s", inpath);
	return exit_client (cptr, cptr, cptr, "No C line for server");
    }
    encr = cptr->passwd;
    if (*aconf->passwd && !StrEq (aconf->passwd, encr)) {
	ircstp->is_ref++;
	sendto_one (cptr, "ERROR :No Access (passwd mismatch) %s", inpath);
	sendto_ops ("Access denied (passwd mismatch) %s", inpath);
	return exit_client (cptr, cptr, cptr, "Bad Password");
    }
    bzero (cptr->passwd, sizeof (cptr->passwd));

#ifndef	HUB
    for (i = 0; i <= highest_fd; i++)
	if (local[i] && IsServer (local[i])) {
	    ircstp->is_ref++;
	    sendto_one (cptr, "ERROR :I'm a leaf not a hub");
	    return exit_client (cptr, cptr, cptr, "I'm a leaf");
	}
#endif
    synchmode = 1;

    /* Check for bitflags here */

    if ((ver = (char *) strchr (cptr->version, '_'))) {
	if (strlen (ver) > 4 && !strncmp (ver, "_cap", 4)) {
	    verflags = atoi (ver + 4);

	    if (verflags & VERSION_ZIPLINKS)
		cptr->flags2 |= FLAGS2_CAPAB_ZIP;

	    sendto_ops
		("Synching with %s using bitflag protocol. Bitflag is %i.",
		 cptr->name, verflags);
	}
	else {
	    sendto_ops ("Suspicious version from server %s (%s)", cptr->name,
			cptr->version);
	}
    }

    if (IsUnknown (cptr)) {

	sendto_one (cptr, "PROTOCTL %s", PROTOCTL_SUPPORTED);

	if (bconf->passwd[0])
	    sendto_one (cptr, "PASS :%s", bconf->passwd);
	/*
	 * Pass my info to the new server
	 */


	sendto_one (cptr, "SERVER %s 1 %s_cap%i :%s",
		    my_name_for_link (me.name, aconf), version, VERSION_SEND,
		    (me.info[0]) ? (me.info) : "AstralIRCd Next-Generation Server");

    }
    else {
	s = (char *) index (aconf->host, '@');
	*s = '\0';		  /* should never be NULL */
	Debug ((DEBUG_INFO, "Check Usernames [%s]vs[%s]",
		aconf->host, cptr->username));
	if (match (aconf->host, cptr->username)) {
	    *s = '@';
	    ircstp->is_ref++;
	    sendto_ops ("Username mismatch [%s]v[%s] : %s",
			aconf->host, cptr->username, get_client_name (cptr,
								      TRUE));
	    sendto_one (cptr, "ERROR :No Username Match");
	    return exit_client (cptr, cptr, cptr, "Bad User");
	}
	*s = '@';
    }

#ifdef ZIP_LINKS

    if ((cptr->flags2 & FLAGS2_CAPAB_ZIP) &&
	(bconf->status != CONF_NZCONNECT_SERVER)) {
	strcat (link_caps, " ZIP_LINKS");
	if (zip_init (cptr) == -1) {
	    zip_free (cptr);
	    sendto_realops ("Unable to setup compressed link for %s",
			    get_client_name (cptr, TRUE));
	    return exit_client (cptr, cptr, &me, "zip_init() failed");
	}

	cptr->flags2 |= (FLAGS2_ZIP | FLAGS2_ZIPFIRST);
    }
    else
	cptr->flags2 &= ~FLAGS2_CAPAB_ZIP;
#endif

    det_confs_butmask (cptr,
		       CONF_HUB | CONF_NOCONNECT_SERVER | CONF_UWORLD);
    /*
       ** *WARNING*
       **   In the following code in place of plain server's
       **   name we send what is returned by get_client_name
       **   which may add the "sockhost" after the name. It's
       **   *very* *important* that there is a SPACE between
       **   the name and sockhost (if present). The receiving
       **   server will start the information field from this
       **   first blank and thus puts the sockhost into info.
       **   ...a bit tricky, but you have been warned, besides
       **   code is more neat this way...  --msa
     */
    SetServer (cptr);
    if (find_conf_host (cptr->confs, cptr->name, CONF_UWORLD))
	cptr->flags |= FLAGS_ULINE;
    cptr->flags |= FLAGS_TS8;
    nextping = time (NULL);
#ifdef HUB
    sendto_serv_butone (&me, ":%s GLOBOPS :Link with %s established.",
			me.name, inpath);
#endif
    sendto_locfailops ("Link with %s established.", inpath);
    /* Insert here */
    (void) add_to_client_hash_table (cptr->name, cptr);
    /* doesnt duplicate cptr->serv if allocted this struct already */
    (void) make_server (cptr);
    (void) strcpy (cptr->serv->up, me.name);
    cptr->srvptr = &me;
    cptr->serv->nline = aconf;
    if (find_conf_host (cptr->confs, cptr->name, CONF_UWORLD))
	cptr->flags |= FLAGS_ULINE;

#ifdef ZIP_LINKS
    cptr->flags2 |= FLAGS2_CBURST;
#endif

    strcpy (cptr->sockhost, "0.0.0.0");

#ifdef	USE_SERVICES
    check_services_butone (SERVICE_WANT_SERVER, sptr,
			   ":%s SERVER %s %d %s :%s", parv[0],
			   cptr->name, hop + 1, cptr->version, cptr->info);
#endif
    /*
       ** Old sendto_serv_but_one() call removed because we now
       ** need to send different names to different servers
       ** (domain name matching) Send new server to other servers.
     */
    for (i = 0; i <= highest_fd; i++) {
	if (!(acptr = local[i]) || !IsServer (acptr) ||
	    acptr == cptr || IsMe (acptr))
	    continue;
	if ((aconf = acptr->serv->nline) &&
	    !match (my_name_for_link (me.name, aconf), cptr->name))
	    continue;

	sendto_one (acptr, ":%s SERVER %s 2 %s :%s",
		    me.name, cptr->name, cptr->version, cptr->info);
    }

    /*
       ** Pass on my client information to the new server
       **
       ** First, pass only servers (idea is that if the link gets
       ** cancelled beacause the server was already there,
       ** there are no NICK's to be cancelled...). Of course,
       ** if cancellation occurs, all this info is sent anyway,
       ** and I guess the link dies when a read is attempted...? --msa
       **
       ** Note: Link cancellation to occur at this point means
       ** that at least two servers from my fragment are building
       ** up connection this other fragment at the same time, it's
       ** a race condition, not the normal way of operation...
       **
       ** ALSO NOTE: using the get_client_name for server names--
       **   see previous *WARNING*!!! (Also, original inpath
       **   is destroyed...)
     */

    aconf = cptr->serv->nline;
    for (acptr = &me; acptr; acptr = acptr->prev) {
	/* acptr->from == acptr for acptr == cptr */
	if (acptr->from == cptr)
	    continue;
	if (IsServer (acptr)) {
	    if (match (my_name_for_link (me.name, aconf), acptr->name) == 0)
		continue;

	    sendto_one (cptr, ":%s SERVER %s %d %s :%s",
			acptr->serv->up, acptr->name,
			acptr->hopcount + 1, acptr->version, acptr->info);
	}
    }

    for (acptr = &me; acptr; acptr = acptr->prev) {
	if (acptr->from == cptr)
            continue;
	if (IsPerson (acptr)) {
	    /*
	    ** IsPerson(x) is true only when IsClient(x) is true.
	    ** These are only true when *BOTH* NICK and USER have
	    ** been received. -avalon
	    ** Apparently USER command was forgotten... -Donwulff
	    */
	    sendto_one (cptr, "%s %s %d %d %s %s %s %lu %s :%s",
	                (IsToken (cptr) ? TOK_SNICK : MSG_SNICK),
			acptr->name, acptr->hopcount + 1, acptr->lastnick,
			acptr->user->username, acptr->user->host,
			acptr->user->server, acptr->user->servicestamp,
			get_mode_str (acptr), acptr->info);
	    if (acptr->user->away)
	        sendto_one (cptr, ":%s %s :%s", acptr->name,
		            (IsToken (cptr) ? TOK_AWAY : MSG_AWAY),
			    acptr->user->away);
	    }
	else if (IsService (acptr)) {
	    sendto_one (cptr, "NICK %s :%d", acptr->name,
	                acptr->hopcount + 1);
	    sendto_one (cptr, ":%s SERVICE * * :%s", acptr->name,
		        acptr->info);
	}
    }

    {
	sendto_ops ("Synching with %s using link options:%s", cptr->name,
		    link_caps);

	for (acptr = &me; acptr; acptr = acptr->prev) {
	    if (acptr->from == cptr)
		continue;
	    if (IsPerson (acptr)) {
		send_user_joins (cptr, acptr);
	    }
	}

    }

    for (chptr = channel; chptr; chptr = chptr->nextch) {
         send_channel_modes_sts (cptr, chptr);

         if (chptr->topic_time)
             sendto_one (cptr, ":%s %s %s %s %lu :%s",
	                 me.name,
			 (IsToken (cptr) ? TOK_TOPIC : MSG_TOPIC),
			 chptr->chname, chptr->topic_nick,
			 chptr->topic_time, chptr->topic);
    }

    synchmode = 0;

    /*
       ** Pass on all services based q-lines
     */

    for (asqline = sqline; asqline; asqline = asqline->next) {
	if (asqline->status != CONF_ILLEGAL) {
	    if (asqline->reason) {
		sendto_one (cptr, ":%s SQLINE %s :%s", me.name,
			    asqline->sqline, asqline->reason);
	    }
	    else {
		sendto_one (cptr, ":%s SQLINE %s", me.name, asqline->sqline);
	    }
	}
    }
    /*
     ** Pass on all services based jinxlines
     */
    for (ajinx = jinx; ajinx; ajinx = ajinx->next) {
	if (ajinx->status != CONF_ILLEGAL) {
	    if (ajinx->reason) {
		sendto_one (cptr, ":%s JINX %s :%s", me.name,
			    ajinx->userhost, ajinx->reason);
	    }
	    else {
		sendto_one (cptr, ":%s JINX %s", me.name, ajinx->userhost);
	    }
	}
    }

#ifdef ZIP_LINKS
    if ((cptr->flags2 & FLAGS2_ZIP) && cptr->zip->out->total_in)
	sendto_realops ("Connect burst to %s: %lu, compressed: %lu (%3.1f%%)",
			get_client_name (cptr, TRUE),
			cptr->zip->out->total_in, cptr->zip->out->total_out,
			(100.0 * (float) cptr->zip->out->total_out) /
			(float) cptr->zip->out->total_in);
#endif
    return 0;
}

/*
   ** m_info
   **   parv[0] = sender prefix
   **   parv[1] = servername
 */
int m_info (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char **text = infotext;

/* Not needed re change to parse.c
   if (check_registered(sptr))
   return 0;
 */

#ifdef SEEUSERSTATS
    if (!IsAnOper (sptr)) {
	if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
	    sendto_umode (UMODE_OPER | UMODE_STATS,
			  "*** Notice -- INFO [truncated] requested by %s (%s@%s)",
			  sptr->name,
			  (sptr->user) ? sptr->user->username : "",
			  (sptr->user) ? sptr->user->host : "");
	}
	else {
	    sendto_umode (UMODE_OPER | UMODE_STATS,
			  "*** Notice -- INFO %s%srequested by %s (%s@%s)",
			  (parc > 1) ? parv[1] : "", (parc > 1) ? " " : "",
			  sptr->name,
			  (sptr->user) ? sptr->user->username : "",
			  (sptr->user) ? sptr->user->host : "");
	}
    }
#endif

/* Send users an error if they try to do /info <something>. -Studded */
    if (parc > 1) {
	sendto_one (sptr, ":%s NOTICE %s :The /info command "
		    "displays the file which lists the names of "
		    "the developers. If you wish to view info on a "
		    "specific nick or channel, use /nickserv info nick or "
		    "/chanserv info #Channel as appropriate.", me.name,
		    parv[0]);
	return -1;
    }
/* Prevent remote /info. It's useless, and could be used to flood the
 * server <> server connections. -Studded
 if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME)
 {
 */
    while (*text)
	sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], *text++);

    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], "");
    sendto_one (sptr,
		":%s %d %s :Birth Date: %s, compile # %s",
		me.name, RPL_INFO, parv[0], creation, generation);
    sendto_one (sptr, ":%s %d %s :On-line since %s",
		me.name, RPL_INFO, parv[0], myctime (me.firsttime));
    sendto_one (sptr, rpl_str (RPL_ENDOFINFO), me.name, parv[0]);
/*      } */

    return 0;
}

/*
 * RPL_NOWON  - Online at the moment (Succesfully added to WATCH-list)
 * RPL_NOWOFF   - Offline at the moement (Succesfully added to WATCH-list)
 * RPL_WATCHOFF - Succesfully removed from WATCH-list.
 * ERR_TOOMANYWATCH - Take a guess :>  Too many WATCH entries.
 */
static void show_watch (aClient *cptr, char *name, int rpl1, int rpl2)
{
    aClient *acptr;


    if ((acptr = find_person (name, NULL)))
	sendto_one (cptr, rpl_str (rpl1), me.name, cptr->name,
		    acptr->name, acptr->user->username,
		    MaskHost (acptr), acptr->lastnick);
    else
	sendto_one (cptr, rpl_str (rpl2), me.name, cptr->name, name, "*", "*",
		    0);
}

/*
 * m_watch
 */
int m_watch (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;
    char *s, **pav = parv, *user;
    char *p = NULL, *def = "l";

/* Not needed re change to parse.c
   if (check_registered(sptr))
   return 0;
 */
    if (parc < 2) {
	/*
	 * Default to 'l' - list who's currently online
	 */
	parc = 2;
	parv[1] = def;
    }
    for (s = (char *) strtoken (&p, *++pav, " "); s;
	 s = (char *) strtoken (&p, NULL, " ")) {
	if ((user = (char *) index (s, '!')))
	    *user++ = '\0';	  /* Not used */

	/*
	 * Prefix of "+", they want to add a name to their WATCH
	 * list.
	 */
	if (*s == '+') {
	    if (do_nick_name (s + 1)) {
		if (sptr->notifies >= MAXWATCH) {
		    sendto_one (sptr, err_str (ERR_TOOMANYWATCH),
				me.name, cptr->name, s + 1);

		    continue;
		}
		add_to_notify_hash_table (s + 1, sptr);
	    }
	    show_watch (sptr, s + 1, RPL_NOWON, RPL_NOWOFF);
	    continue;
	}
	/*
	 * Prefix of "-", coward wants to remove somebody from their
	 * WATCH list.  So do it. :-)
	 */
	if (*s == '-') {
	    del_from_notify_hash_table (s + 1, sptr);
	    show_watch (sptr, s + 1, RPL_WATCHOFF, RPL_WATCHOFF);

	    continue;
	}
	/*
	 * Fancy "C" or "c", they want to nuke their WATCH list and start
	 * over, so be it.
	 */
	if (*s == 'C' || *s == 'c') {
	    hash_del_notify_list (sptr);
	    sendto_one (sptr, rpl_str (RPL_WATCHCLEAR), me.name, parv[0]);

	    continue;
	}
	/*
	 * Now comes the fun stuff, "S" or "s" returns a status report of
	 * their WATCH list.  I imagine this could be CPU intensive if its
	 * done alot, perhaps an auto-lag on this?
	 */
	if (*s == 'S' || *s == 's') {
	    Link *lp;
	    aNotify *anptr;
	    int count = 0;

	    /*
	     * Send a list of how many users they have on their WATCH list
	     * and how many WATCH lists they are on.
	     */
	    anptr = hash_get_notify (sptr->name);
	    if (anptr)
		for (lp = anptr->notify, count = 1; (lp = lp->next); count++);
	    sendto_one (sptr, rpl_str (RPL_WATCHSTAT), me.name, parv[0],
			sptr->notifies, count);

	    /*
	     * Send a list of everybody in their WATCH list. Be careful
	     * not to buffer overflow.
	     */
	    if ((lp = sptr->notify) == NULL) {
		sendto_one (sptr, rpl_str (RPL_ENDOFWATCHLIST), me.name,
			    parv[0], *s);
		continue;
	    }
	    *buf = '\0';
	    strcpy (buf, lp->value.nptr->nick);
	    count = strlen (parv[0]) + strlen (me.name) + 10 + strlen (buf);
	    while ((lp = lp->next)) {
		if (count + strlen (lp->value.nptr->nick) + 1 > BUFSIZE - 2) {
		    sendto_one (sptr, rpl_str (RPL_WATCHLIST), me.name,
				parv[0], buf);
		    *buf = '\0';
		    count = strlen (parv[0]) + strlen (me.name) + 10;
		}
		strcat (buf, " ");
		strcat (buf, lp->value.nptr->nick);
		count += (strlen (lp->value.nptr->nick) + 1);
	    }
	    sendto_one (sptr, rpl_str (RPL_WATCHLIST), me.name, parv[0], buf);

	    sendto_one (sptr, rpl_str (RPL_ENDOFWATCHLIST), me.name, parv[0],
			*s);
	    continue;
	}
	/*
	 * Well that was fun, NOT.  Now they want a list of everybody in
	 * their WATCH list AND if they are online or offline? Sheesh,
	 * greedy arn't we?
	 */
	if (*s == 'L' || *s == 'l') {
	    Link *lp = sptr->notify;

	    while (lp) {
		if ((acptr = find_person (lp->value.nptr->nick, NULL)))
		    sendto_one (sptr, rpl_str (RPL_NOWON), me.name, parv[0],
				acptr->name, acptr->user->username,
				MaskHost (acptr), acptr->lastnick);
		/*
		 * But actually, only show them offline if its a capital
		 * 'L' (full list wanted).
		 */
		else if (isupper (*s))
		    sendto_one (sptr, rpl_str (RPL_NOWOFF), me.name, parv[0],
				lp->value.nptr->nick, "*", "*",
				lp->value.nptr->lasttime);
		lp = lp->next;
	    }

	    sendto_one (sptr, rpl_str (RPL_ENDOFWATCHLIST), me.name, parv[0],
			*s);

	    continue;
	}
	/*
	 * Hmm.. unknown prefix character.. Ignore it. :-)
	 */
    }

    return 0;
}



/*
   ** m_links
   **   parv[0] = sender prefix
   **   parv[1] = servername mask
   ** or
   **   parv[0] = sender prefix
   **   parv[1] = server to query
   **      parv[2] = servername mask
 */
int m_links (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *mask;
    aClient *acptr;

    if (!IsOper(cptr)) {
	    sendto_one ( cptr, "Links is not allowed on this server.");
	    return 0;
    }

    if (parc > 2) {
	if (hunt_server (cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv)
	    != HUNTED_ISME)
	    return 0;
	mask = parv[2];
    }
    else
	mask = parc < 2 ? NULL : parv[1];

    for (acptr = client, (void) collapse (mask); acptr; acptr = acptr->next) {
	if (!IsServer (acptr) && !IsMe (acptr))
	    continue;
	if (!BadPtr (mask) && match (mask, acptr->name))
	    continue;
	sendto_one (sptr, rpl_str (RPL_LINKS),
		    me.name, parv[0], acptr->name, acptr->serv->up,
		    acptr->hopcount, (acptr->info[0] ? acptr->info :
				      "(Unknown Location)"));
    }

    sendto_one (sptr, rpl_str (RPL_ENDOFLINKS), me.name, parv[0],
		BadPtr (mask) ? "*" : mask);
    return 0;
}

/*
   ** m_stats
   **   parv[0] = sender prefix
   **   parv[1] = statistics selector (defaults to Message frequency)
   **   parv[2] = server name (current server defaulted, if omitted)
   **
   **   Currently supported are:
   **       M = Message frequency (the old stat behaviour)
   **       L = Local Link statistics
   **              C = Report C and N configuration lines
 */
/*
   ** m_stats/stats_conf
   **    Report N/C-configuration lines from this server. This could
   **    report other configuration lines too, but converting the
   **    status back to "char" is a bit akward--not worth the code
   **    it needs...
   **
   **    Note:   The info is reported in the order the server uses
   **            it--not reversed as in ircd.conf!
 */

static int report_array[19][3] = {
    {CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
    {CONF_NZCONNECT_SERVER, RPL_STATSCLINE, 'c'},
    {CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
    {CONF_CLIENT, RPL_STATSILINE, 'I'},
    {CONF_KILL, RPL_STATSKLINE, 'K'},
    {CONF_ZAP, RPL_STATSKLINE, 'Z'},
    {CONF_QUARANTINED_NICK, RPL_STATSQLINE, 'Q'},
    {CONF_OPERATOR, RPL_STATSOLINE, 'O'},
    {CONF_HUB, RPL_STATSHLINE, 'H'},
    {CONF_LOCOP, RPL_STATSOLINE, 'o'},
    {CONF_SERVICE, RPL_STATSSLINE, 'S'},
    {CONF_UWORLD, RPL_STATSULINE, 'U'},
    {CONF_MISSING, RPL_STATSXLINE, 'X'},
    {CONF_ZTIME, RPL_STATSFLINE, 'F'},
    {CONF_DCCBLOCK, RPL_STATSBLINE, 'E'},
    {0, 0}
};

static void report_sqlined_nicks (aClient *sptr)
{
    aSqlineItem *tmp;
    char *nickmask, *reason;

    for (tmp = sqline; tmp; tmp = tmp->next) {
	if (tmp->status != CONF_ILLEGAL) {
	    nickmask = BadPtr (tmp->sqline) ? "<NULL>" : tmp->sqline;
	    reason = BadPtr (tmp->reason) ? "<NULL>" : tmp->reason;
	    sendto_one (sptr, rpl_str (RPL_SQLINE_NICK), me.name,
			sptr->name, nickmask, reason);
	}
    }
}

static void report_configured_links (aClient *sptr, int mask)
{
    static char null[] = "<NULL>";
    aConfItem *tmp;
    int *p, port, tmpmask;
    char c, *host, *pass, *name;

    tmpmask = (mask == CONF_MISSING) ? CONF_CONNECT_SERVER : mask;

    for (tmp = conf; tmp; tmp = tmp->next)
	if (tmp->status & tmpmask) {
	    for (p = &report_array[0][0]; *p; p += 3)
		if (*p == tmp->status)
		    break;
	    if (!*p)
		continue;
	    c = (char) *(p + 2);
	    host = BadPtr (tmp->host) ? null : tmp->host;
	    pass = BadPtr (tmp->passwd) ? null : tmp->passwd;
	    name = BadPtr (tmp->name) ? null : tmp->name;
	    port = (int) tmp->port;
	    /*
	     * On K line the passwd contents can be
	     * displayed on STATS reply.    -Vesa
	     */
	    /* Same with Z-lines and q/Q-lines -- Barubary */
	    if ((tmp->status == CONF_KILL) || (tmp->status & CONF_QUARANTINE)
		|| (tmp->status == CONF_ZAP)) {
/* These mods are to tell the difference between the different kinds
 * of klines.  the only effect it has is in the display.  --Russell
 */
/* Now translates spaces to _'s to show comments in klines -- Barubary */
		char *temp;
		if (!pass)
		    strcpy (buf, "<NULL>");
		else {
		    strcpy (buf, pass);
		    for (temp = buf; *temp; temp++)
			if (*temp == ' ')
			    *temp = '_';
		}
		/* semicolon intentional -- Barubary */
		if (tmp->status == CONF_QUARANTINED_NICK);
		/* Hide password for servers -- Barubary */
		else if (tmp->status & CONF_QUARANTINE)
		    strcpy (buf, "*");
		else {
/* This wasn't documented before - comments aren't displayed for akills
   because they are all the same. -- Barubary */
		    if (tmp->tmpconf == KLINE_AKILL)
			strcpy (buf, "*");
		    /* KLINE_PERM == 0 - watch out when doing
		       Z-lines. -- Barubary */
		    if (tmp->status != CONF_ZAP) {
			if (tmp->tmpconf == KLINE_PERM)
			    c = 'K';
			if (tmp->tmpconf == KLINE_TEMP)
			    c = 'k';
			if (tmp->tmpconf == KLINE_AKILL)
			    c = 'A';
		    }
		    else {
			if (tmp->tmpconf == KLINE_PERM)
			    c = 'Z';
			if (tmp->tmpconf == KLINE_TEMP)
			    c = 'z';
			if (tmp->tmpconf == KLINE_AKILL)
			    c = 'S';
		    }
		}
		sendto_one (sptr, rpl_str (p[1]), me.name,
			    sptr->name, c, host,
			    buf, name, port, get_conf_class (tmp));
	    }
	    /* z-line times     -taz */
	    else if (tmp->status & CONF_ZTIME)
		sendto_one (sptr, rpl_str (p[1]), me.name, sptr->name, host);
	    /* Only display on X if server is missing */
	    else if (mask == CONF_MISSING) {
		if (!find_server (name, NULL))
		    sendto_one (sptr, rpl_str (RPL_STATSXLINE), me.name,
				sptr->name, name, port);
	    }
	    else {
		if (!IsOper (sptr) && (mask & CONF_NOCONNECT_SERVER ||
				       mask & CONF_CONNECT_SERVER))
		    sendto_one (sptr, rpl_str (p[1]), me.name,
				sptr->name, c, "*", name, port,
				get_conf_class (tmp));
		else
		    sendto_one (sptr, rpl_str (p[1]), me.name,
				sptr->name, c, host, name, port,
				get_conf_class (tmp));
	    }
	}
    return;
}

static void report_jinx (aClient * sptr)
{
    aJinxItem *ajinx;
    if (!IsAnOper (sptr))
	return;
    for (ajinx = jinx; ajinx; ajinx = ajinx->next) {
	sendto_one (sptr, rpl_str (RPL_STATSJINX), me.name,
		    sptr->name, ajinx->userhost,
		    ajinx->reason ? ajinx->reason : "*");
    }
}


/* Used to blank out ports -- Barubary */
char *get_client_name2 (aClient * acptr, int showports)
{
    char *pointer = get_client_name (acptr, TRUE);

    if (!pointer)
	return NULL;
    if (showports)
	return pointer;
    if (!(char *) strrchr (pointer, '.'))
	return NULL;
    strcpy ((char *) strrchr (pointer, '.'), ".0]");

    return pointer;
}

int m_stats (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
#ifndef DEBUGMODE
    static char Sformat[] =
	":%s %d %s SendQ SendM SendBytes RcveM RcveBytes Open_since :Idle";
    static char Lformat[] = ":%s %d %s %s %u %u %u %u %u %u :%u";
#else
    static char Sformat[] =
	":%s %d %s SendQ SendM SendBytes RcveM RcveBytes Open_since CPU :Idle";
    static char Lformat[] = ":%s %d %s %s %u %u %u %u %u %u %s";
    char pbuf[96]; /* Should be enough for to ints */
#endif
    struct Message *mptr;
    aClient *acptr;
    char stat = parc > 1 ? parv[1][0] : '\0';
    int i;
    int doall = 0, wilds = 0, showports = IsAnOper (sptr), remote = 0;
    char *name;

/* Not needed re change to parse.c
   if (check_registered(sptr))
   return 0;
 */
    if (hunt_server (cptr, sptr, ":%s STATS %s :%s", 2, parc, parv) !=
	HUNTED_ISME)
	return 0;

#ifdef SEEUSERSTATS
    if (!IsAnOper (sptr)) {
	if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
	    sendto_umode (UMODE_OPER | UMODE_STATS,
			  "*** Notice -- STATS [truncated] requested by %s (%s@%s)",
			  sptr->name,
			  (sptr->user) ? sptr->user->username : "",
			  (sptr->user) ? sptr->user->host : "");
	}
	else {
	    sendto_umode (UMODE_OPER | UMODE_STATS,
			  "*** Notice -- STATS %s%srequested by %s (%s@%s)",
			  (parc > 1) ? parv[1] : "", (parc > 1) ? " " : "",
			  sptr->name,
			  (sptr->user) ? sptr->user->username : "",
			  (sptr->user) ? sptr->user->host : "");
	}
    }
#endif

    if (parc > 2) {
	name = parv[2];
	if (!mycmp (name, me.name))
	    doall = 2;
	else if (match (name, me.name) == 0)
	    doall = 1;
	if (index (name, '*') || index (name, '?'))
	    wilds = 1;
    }
    else
	name = me.name;

/* Add better error checking for common user mistakes. Use a
 * NOTICE instead of a numeric because more clients can see
 * and display them properly. -Studded */

    if (!(stat)) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
		    me.name, parv[0], "STATS");
	return -1;
    }
    if ((parc == 2) && (parv[1][1])) {
	sendto_one (sptr, ":%s NOTICE %s :You have requested a stat "
		    "(%s) that does not exist.", me.name, parv[0], parv[1]);
	return -1;
    }
    switch (stat) {
    case 'L':
    case 'l':
	/*
	 * send info about connections which match, or all if the
	 * mask matches me.name.  Only restrictions are on those who
	 * are invisible not being visible to 'foreigners' who use
	 * a wild card based search to list it.
	 */

	if (!IsAnOper (sptr))
	    break;

	sendto_one (sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
	if (IsServer (cptr)) {
	    remote = 1;
	    wilds = 0;
	}
	for (i = 0; i <= highest_fd; i++) {
	    if (!(acptr = local[i]))
		continue;
	    if (IsInvisible (acptr) && (doall || wilds) &&
		!(MyConnect (sptr) && IsOper (sptr)) &&
		!IsAnOper (acptr) && (acptr != sptr))
		continue;
	    if (remote && doall && !IsServer (acptr) && !IsMe (acptr))
		continue;
	    if (remote && !doall && IsServer (acptr))
		continue;
	    if (!doall && wilds && match (name, acptr->name))
		continue;
	    if (!(parc == 2 && IsServer (acptr)) &&
		!(doall || wilds) && mycmp (name, acptr->name))
		continue;
#ifdef DEBUGMODE
	    sprintf (pbuf, "%d :%d", acptr->cputime,
		     (acptr->user && MyConnect (acptr)) ?
		     time (NULL) - acptr->user->last : 0);
#endif
	    if (IsOper (sptr))
		sendto_one (sptr, Lformat, me.name,
			    RPL_STATSLINKINFO, parv[0],
			    (isupper (stat)) ?
			    get_client_name2 (acptr, showports) :
			    get_client_name (acptr, FALSE),
			    (int) DBufLength (&acptr->sendQ),
			    (int) acptr->sendM, (int) acptr->sendK,
			    (int) acptr->receiveM, (int) acptr->receiveK,
			    time (NULL) - acptr->firsttime,
#ifndef DEBUGMODE
			    (acptr->user && MyConnect (acptr)) ?
			    time (NULL) - acptr->user->last : 0);
#else
			    pbuf);
#endif
	    else if (!strchr (acptr->name, '.'))
		sendto_one (sptr, Lformat, me.name,
			    RPL_STATSLINKINFO, parv[0],
			    (isupper (stat)) ?
			    get_client_name2 (acptr, showports) :
			    get_client_name (acptr, FALSE),
			    (int) DBufLength (&acptr->sendQ),
			    (int) acptr->sendM, (int) acptr->sendK,
			    (int) acptr->receiveM, (int) acptr->receiveK,
			    time (NULL) - acptr->firsttime,
#ifndef DEBUGMODE
			    (acptr->user && MyConnect (acptr)) ?
			    time (NULL) - acptr->user->last : 0);
#else
			    pbuf);
#endif
	}
	break;
    case 'C':
    case 'c':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_CONNECT_SERVER |
				     CONF_NOCONNECT_SERVER |
				     CONF_NZCONNECT_SERVER);
	break;
    case 'H':
    case 'h':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_HUB);
	break;
    case 'I':
    case 'i':
	report_configured_links (sptr, CONF_CLIENT);
	break;
    case 'K':
	if (IsOper (sptr))
            report_configured_links (sptr, CONF_KILL | CONF_ZAP | CONF_KILLEXEMPT);
	else
            report_configured_links (sptr, CONF_KILL | CONF_ZAP);
        break;
    case 'k':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_KILL | CONF_KILLEXEMPT);
	else
	    report_configured_links (sptr, CONF_KILL);

	break;
    case 'M':
    case 'm':
	if (IsOper (sptr)) {
	    for (mptr = msgtab; mptr->cmd; mptr++)
		if (mptr->count)
#ifndef DEBUGMODE
		    sendto_one (sptr, rpl_str (RPL_STATSCOMMANDS),
				me.name, parv[0], mptr->cmd, mptr->count,
				mptr->bytes);
#else
		    sendto_one (sptr, rpl_str (RPL_STATSCOMMANDS),
				me.name, parv[0], mptr->cmd,
				mptr->count, mptr->bytes,
				mptr->lticks,
				mptr->lticks / CLOCKS_PER_SEC,
				mptr->rticks, mptr->rticks / CLOCKS_PER_SEC);
#endif
	}
	else {
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	}
	break;
    case 'o':
    case 'O':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_OPS);
	else
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	break;
    case 'Q':
	report_configured_links (sptr, CONF_QUARANTINE);
	break;
    case 'q':
	report_sqlined_nicks (sptr);
	break;
    case 'R':
    case 'r':
#ifdef DEBUGMODE
	send_usage (sptr, parv[0]);
#endif
	break;
    case 'S':
    case 's':
	report_configured_links (sptr, CONF_SERVICE);
	break;
    case 'T':
    case 't':
	tstats (sptr, parv[0]);
	break;
    case 'U':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_UWORLD);
	else
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	break;
    case 'f':
    case 'F':
	report_configured_links (sptr, CONF_ZTIME);
	break;
    case 'u':
	{
	    register time_t tmpnow;

	    tmpnow = time (NULL) - me.since;
	    sendto_one (sptr, rpl_str (RPL_STATSUPTIME), me.name, parv[0],
			tmpnow / 86400, (tmpnow / 3600) % 24,
			(tmpnow / 60) % 60, tmpnow % 60);
	    sendto_one (sptr, rpl_str (RPL_STATSCONN), me.name, parv[0],
			max_connection_count, max_client_count);
	    break;
	}
    case 'W':
    case 'w':
	if (IsOper (sptr))
	    calc_load (sptr, parv[0]);
	else
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	break;
    case 'X':
    case 'x':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_MISSING);
	else
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	break;
    case 'Y':
    case 'y':
	report_classes (sptr);
	break;
    case 'Z':
        report_configured_links (sptr, CONF_ZAP);
        break;
    case 'z':
	count_memory (sptr, parv[0]);
	break;
    case 'J':
    case 'j':
	if (IsOper (sptr))
	    report_jinx (sptr);
	else
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	break;
    case '?':
	serv_info (sptr, parv[0]);
	break;
    case 'E':
    case 'e':
	if (IsOper (sptr))
	    report_configured_links (sptr, CONF_DCCBLOCK);
	else
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	break;
    default:
	sendto_one (sptr, ":%s NOTICE %s :You have requested a stat "
		    "(%c) that does not exist.", me.name, parv[0], stat);
	break;
    }
    sendto_one (sptr, rpl_str (RPL_ENDOFSTATS), me.name, parv[0], stat);
    return 0;
}

/*
   ** Note: At least at protocol level ERROR has only one parameter,
   ** although this is called internally from other functions
   ** --msa
   **
   **   parv[0] = sender prefix
   **   parv[*] = parameters
 */
int m_error (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *para;

    para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>";

    Debug ((DEBUG_ERROR, "Received ERROR message from %s: %s",
	    sptr->name, para));
    /*
       ** Ignore error messages generated by normal user clients
       ** (because ill-behaving user clients would flood opers
       ** screen otherwise). Pass ERROR's from other sources to
       ** the local operator...
     */
    if (IsPerson (cptr) || IsUnknown (cptr) || IsService (cptr))
	return 0;
    if (cptr == sptr) {
	sendto_serv_butone (&me, ":%s GLOBOPS :ERROR from %s -- %s",
			    me.name, get_client_name (cptr, FALSE), para);
	sendto_locfailops ("ERROR :from %s -- %s",
			   get_client_name (cptr, FALSE), para);
    }
    else {
	sendto_serv_butone (&me, ":%s GLOBOPS :ERROR from %s via %s -- %s",
			    me.name, sptr->name, get_client_name (cptr,
								  FALSE),
			    para);
	sendto_ops ("ERROR :from %s via %s -- %s", sptr->name,
		    get_client_name (cptr, FALSE), para);
    }
    return 0;
}

/*
   ** m_help
   **      parv[0] = sender prefix
 */
int m_help(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int i;
    if (MyClient (sptr)) {
#ifdef SEEUSERSTATS
	if (!IsAnOper (sptr)) {
	    if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
		sendto_umode (UMODE_OPER | UMODE_STATS,
			      "*** Notice -- HELP [truncated] requested by %s (%s@%s)",
			      sptr->name,
			      (sptr->user) ? sptr->user->username : "",
			      (sptr->user) ? sptr->user->host : "");
	    }
	    else {
		sendto_umode (UMODE_OPER | UMODE_STATS,
			      "*** Notice -- HELP %s%srequested by %s (%s@%s)",
			      (parc > 1) ? parv[1] : "",
			      (parc > 1) ? " " : "", sptr->name,
			      (sptr->user) ? sptr->user->username : "",
			      (sptr->user) ? sptr->user->host : "");
	    }
	}
#endif
	sendto_one (sptr, ":%s NOTICE %s :Full command list:",
		    me.name, parv[0]);
	sendto_one (sptr, ":%s NOTICE %s :command token", me.name, parv[0]);
	sendto_one (sptr, ":%s NOTICE %s :=============", me.name, parv[0]);
	for (i = 0; msgtab[i].cmd; i++)
	    sendto_one (sptr, ":%s NOTICE %s :%s %s",
			me.name, parv[0], msgtab[i].cmd, msgtab[i].token);
	sendto_one (sptr, ":%s NOTICE %s :=============", me.name, parv[0]);
	return 0;
    }
    return -1;
}

/*
 * parv[0] = sender
 * parv[1] = host/server mask.
 * parv[2] = server to query
 */
int m_lusers (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int s_count, c_count, u_count, i_count;
    int o_count, m_client, m_client_local, m_server;
    char mydom_mask[HOSTLEN + 1];
    aClient *acptr;

/* Not needed re change to parse.c
   if (check_registered_user(sptr))
   return 0;
 */
    if (parc > 2)
	if (hunt_server (cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv)
	    != HUNTED_ISME)
	    return 0;

    mydom_mask[0] = '*';
    strncpy (&mydom_mask[1], DOMAINNAME, HOSTLEN - 1);

    if (parc == 1) {
	s_count = lu_serv;
	c_count = lu_noninv;
	u_count = lu_unknown;
	i_count = lu_inv;
	o_count = lu_oper;
	m_client = lu_lu;
	m_client_local = lu_lulocal;
	m_server = lu_lserv;
	global_count = lu_cglobalu;
    }
    else {
	s_count = c_count = u_count = i_count = o_count = m_client =
	    m_client_local = m_server = 0;
	(void) collapse (parv[1]);
	for (acptr = client; acptr; acptr = acptr->next) {
	    if (parc > 1) {
		if (!IsServer (acptr) && acptr->user) {
		    if (match (parv[1], acptr->user->server))
			continue;
		}
		else {
		    if (match (parv[1], acptr->name))
			continue;
		}
	    }
	    switch (acptr->status) {
	    case STAT_SERVER:
		if (MyConnect (acptr))
		    m_server++;
	    case STAT_ME:
		s_count++;
		break;
	    case STAT_CLIENT:
		if (IsOper (acptr))
		    o_count++;
		if (MyConnect (acptr)) {
		    m_client++;
		    if (match (mydom_mask, acptr->sockhost) == 0)
			m_client_local++;
		}
		if (!IsInvisible (acptr))
		    c_count++;
		else
		    i_count++;
		break;
	    default:
		u_count++;
		break;
	    }
	}
    }
    sendto_one (sptr, rpl_str (RPL_LUSERCLIENT), me.name, parv[0],
		c_count, i_count, s_count);
    max_client_count = lu_mlu;
    max_global_count = lu_mglobalu;

    if (o_count)
	sendto_one (sptr, rpl_str (RPL_LUSEROP), me.name, parv[0], o_count);
    if (u_count > 0)
	sendto_one (sptr, rpl_str (RPL_LUSERUNKNOWN), me.name, parv[0],
		    u_count);
    if ((c_count = count_channels (sptr)) > 0)
	sendto_one (sptr, rpl_str (RPL_LUSERCHANNELS),
		    me.name, parv[0], lu_channel);
    sendto_one (sptr, rpl_str (RPL_LUSERME),
		me.name, parv[0], m_client, m_server);
    sendto_one (sptr, rpl_str (RPL_LOCALUSERS),
		me.name, parv[0], m_client, max_client_count);
    sendto_one (sptr, rpl_str (RPL_GLOBALUSERS),
		me.name, parv[0], global_count, max_global_count);
    sendto_one (sptr, rpl_str (RPL_STATSCONN),
		me.name, parv[0], max_connection_count, max_client_count);
    if ((m_client + m_server) > max_connection_count) {
	max_connection_count = m_client + m_server;
	if (max_connection_count % 10 == 0)	/* only send on even tens */
	    sendto_ops ("Maximum connections: %d (%d clients)",
			max_connection_count, max_client_count);
    }
    current_load_data.local_count = m_client_local;
    current_load_data.client_count = m_client;
    current_load_data.conn_count = m_client + m_server;
    return 0;
}


/***********************************************************************
 * m_connect() - Added by Jto 11 Feb 1989
 ***********************************************************************/

/*
   ** m_connect
   **   parv[0] = sender prefix
   **   parv[1] = servername
   **   parv[2] = port number
   **   parv[3] = remote server
 */
int m_connect (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int port, tmpport, retval;
    aConfItem *aconf, *cconf;
    aClient *acptr;

    if (check_registered (sptr))
	return 0;

    if (!IsPrivileged (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return -1;
    }
    if (MyClient (sptr) && !OPCanGRoute (sptr) && parc > 3) {	/* Only allow LocOps to make */
	/* local CONNECTS --SRB      */
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    if (MyClient (sptr) && !OPCanLRoute (sptr) && parc <= 3) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    if (hunt_server (cptr, sptr, ":%s CONNECT %s %s :%s",
		     3, parc, parv) != HUNTED_ISME)
	return 0;

    if (parc < 2 || *parv[1] == '\0') {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
		    me.name, parv[0], "CONNECT");
	return -1;
    }
    if ((acptr = find_server (parv[1], NULL))) {
	sendto_one (sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
		    me.name, parv[0], parv[1], "already exists from",
		    acptr->from->name);
	return 0;
    }
    for (aconf = conf; aconf; aconf = aconf->next)
	if ((aconf->status == CONF_CONNECT_SERVER ||
	     aconf->status == CONF_NZCONNECT_SERVER) &&
	    match (parv[1], aconf->name) == 0)
	    break;
    /* Checked first servernames, then try hostnames. */
    if (!aconf)
	for (aconf = conf; aconf; aconf = aconf->next)
	    if (aconf->status == CONF_CONNECT_SERVER &&
		(match (parv[1], aconf->host) == 0 ||
		 match (parv[1], index (aconf->host, '@') + 1) == 0))
		break;

    if (!aconf) {
	sendto_one (sptr,
		    "NOTICE %s :Connect: Host %s not listed in ircd.conf",
		    parv[0], parv[1]);
	return 0;
    }
    /*
       ** Get port number from user, if given. If not specified,
       ** use the default form configuration structure. If missing
       ** from there, then use the precompiled default.
     */
    tmpport = port = aconf->port;
    if (parc > 2 && !BadPtr (parv[2])) {
	if ((port = atoi (parv[2])) <= 0) {
	    sendto_one (sptr, "NOTICE %s :Connect: Illegal port number",
			parv[0]);
	    return 0;
	}
    }
    else if (port <= 0 && (port = PORTNUM) <= 0) {
	sendto_one (sptr, ":%s NOTICE %s :Connect: missing port number",
		    me.name, parv[0]);
	return 0;
    }
    /*
       ** Notify all operators about remote connect requests
     */
    if (!IsAnOper (cptr)) {
	sendto_serv_butone (&me,
			    ":%s GLOBOPS :Remote CONNECT %s %s from %s",
			    me.name, parv[1], parv[2] ? parv[2] : "",
			    get_client_name (sptr, FALSE));
#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
	syslog (LOG_DEBUG, "CONNECT From %s : %s %d", parv[0], parv[1],
		parv[2] ? parv[2] : "");
#endif
    }
    aconf->port = port;
    switch (retval = connect_server (aconf, sptr, NULL)) {
    case 0:
	sendto_one (sptr,
		    ":%s NOTICE %s :*** Connecting to %s[%s].",
		    me.name, parv[0], aconf->host, aconf->name);
	break;
    case -1:
	sendto_one (sptr, ":%s NOTICE %s :*** Couldn't connect to %s.",
		    me.name, parv[0], aconf->host);
	break;
    case -2:
	sendto_one (sptr, ":%s NOTICE %s :*** Host %s is unknown.",
		    me.name, parv[0], aconf->host);
	break;
    default:
	sendto_one (sptr,
		    ":%s NOTICE %s :*** Connection to %s failed: %s",
		    me.name, parv[0], aconf->host, strerror (retval));
    }
    aconf->port = tmpport;
    return 0;
}

/* m_gnotice  (Russell) sort of like wallop, but only to +g clients on
   ** this server.
   **   parv[0] = sender prefix
   **   parv[1] = message text
 */
int m_gnotice (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *message;
/*  char *pv[4]; Compiler says this is not used */

    if (check_registered (sptr))
	return 0;

    message = parc > 1 ? parv[1] : NULL;

    if (BadPtr (message)) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
		    me.name, parv[0], "GNOTICE");
	return 0;
    }
    if (!IsServer (sptr) && MyConnect (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    sendto_serv_butone (IsServer (cptr) ? cptr : NULL, ":%s GNOTICE :%s",
			parv[0], message);
    sendto_failops ("from %s: %s", parv[0], message);
    return 0;
}

/*
   ** m_globops (write to opers who are +g currently online)
   **      parv[0] = sender prefix
   **      parv[1] = message text
 */
int m_globops (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *message;
/*  char    *pv[4]; Compiler says this is not used */

    if (check_registered (sptr))
	return 0;

    message = parc > 1 ? parv[1] : NULL;

    if (BadPtr (message)) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
		    me.name, parv[0], "GLOBOPS");
	return 0;
    }
    if (MyClient (sptr) && !OPCanGlobOps (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    sendto_serv_butone (IsServer (cptr) ? cptr : NULL,
			":%s GLOBOPS :%s", parv[0], message);
    sendto_failops_whoare_opers ("from %s: %s", parv[0], message);

    return 0;
}

/*
   ** m_locops (write to opers who are +g currently online *this* server)
   **      parv[0] = sender prefix
   **      parv[1] = message text
 */
int m_locops (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *message;
/*  char    *pv[4]; Compiler says this is not used */

    if (check_registered_user (cptr))
	return 0;

    message = parc > 1 ? parv[1] : NULL;

    if (BadPtr (message)) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
		    me.name, parv[0], "LOCOPS");
	return 0;
    }
    if (MyClient (sptr) && !OPCanLocOps (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    sendto_locfailops ("from %s: %s", parv[0], message);

    return 0;
}

/* m_goper  (Russell) sort of like wallop, but only to ALL +o clients on
   ** every server.
   **      parv[0] = sender prefix
   **      parv[1] = message text
 */
int m_goper (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *message;
/*  char *pv[4]; Compiler says this is not used */

    if (check_registered (sptr))
	return 0;

    message = parc > 1 ? parv[1] : NULL;

    if (BadPtr (message)) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
		    me.name, parv[0], "GOPER");
	return 0;
    }
/*      if (!IsServer(sptr) && MyConnect(sptr) && !IsAnOper(sptr)) */
    if (!IsServer (sptr) || !IsULine (cptr, sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    sendto_serv_butone (IsServer (cptr) ? cptr : NULL, ":%s GOPER :%s",
			parv[0], message);
    sendto_opers ("from %s: %s", parv[0], message);
    return 0;
}

/*
   ** m_time
   **   parv[0] = sender prefix
   **   parv[1] = servername
 */
int m_time (aClient *cptr, aClient *sptr, int parc, char *parv[])
{

    time_t t1;
    int ticks;

    ticks = time (&t1);

    if (hunt_server (cptr, sptr, ":%s TIME :%s", 1, parc, parv) ==
	HUNTED_ISME)
	sendto_one (sptr, rpl_str (RPL_TIME), me.name, parv[0], me.name,
		    myctime (ticks));
    return 0;
}


/*
   ** m_admin
   **   parv[0] = sender prefix
   **   parv[1] = servername
 */
int m_admin (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aConfItem *aconf;

    /* Make sure they stick to the local server if not registered.
     *  -Studded */
    if ((!IsRegistered (cptr)) && (!BadPtr (parv[1]))) {
	sendto_one (cptr, ":%s %d ADMIN :You have not registered",
		    me.name, ERR_NOTREGISTERED);
	return -1;
    }
    /* This probably isn't needed anymore, but I don't
     * understand it. -Studded 23 May 1998 */

    /* Only allow remote ADMINs if registered -- Barubary */
    if (IsPerson (sptr) || IsServer (cptr))
	if (hunt_server (cptr, sptr, ":%s ADMIN :%s", 1, parc, parv) !=
	    HUNTED_ISME)
	    return 0;

#ifdef SEEUSERSTATS
    if (!IsAnOper (sptr)) {
	if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
	    sendto_umode (UMODE_OPER | UMODE_STATS,
			  "*** Notice -- ADMIN [truncated] requested by %s (%s@%s)",
			  sptr->name,
			  (sptr->user) ? sptr->user->username : "",
			  (sptr->user) ? sptr->user->host : "");
	}
	else {
	    sendto_umode (UMODE_OPER | UMODE_STATS,
			  "*** Notice -- ADMIN %s%srequested by %s (%s@%s)",
			  (parc > 1) ? parv[1] : "", (parc > 1) ? " " : "",
			  sptr->name,
			  (sptr->user) ? sptr->user->username : "",
			  (sptr->user) ? sptr->user->host : "");
	}
    }
#endif

    if ((aconf = find_admin ())) {
	sendto_one (sptr, rpl_str (RPL_ADMINME), me.name, parv[0], me.name);
	sendto_one (sptr, rpl_str (RPL_ADMINLOC1),
		    me.name, parv[0], (aconf->host ? aconf->host : "-"));
	sendto_one (sptr, rpl_str (RPL_ADMINLOC2),
		    me.name, parv[0], (aconf->passwd ? aconf->passwd : "-"));
	sendto_one (sptr, rpl_str (RPL_ADMINEMAIL),
		    me.name, parv[0], (aconf->name ? aconf->name : "-"));
    }
    else
	sendto_one (sptr, err_str (ERR_NOADMININFO), me.name, parv[0],
		    me.name);
    return 0;
}

/*
   ** m_rehash
   **
 */
int m_rehash (aClient *cptr, aClient *sptr, int parc, char *parv[])
{

    /*
     * To allow netwide rehash, it's needed to allow rehash between servers
     * for both Services (netwide) and SRA (server specific) -- Rodrigo
     */
    if (IsULine (cptr, sptr) || IsSRoot(sptr)) {
    	if (hunt_server (cptr, sptr, ":%s REHASH :%s", 1, parc, parv) !=
	    HUNTED_ISME)
	    return 0;
    }

    if(!IsULine (cptr, sptr) && !IsSRoot(sptr)) {
        if (!MyClient (sptr) || !OPCanRehash (sptr)) {
	    sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	    return 0;
        }
    }

    sendto_one (sptr, rpl_str (RPL_REHASHING), me.name, parv[0], configfile, me.name);
    sendto_ops ("%s is rehashing Server config file", parv[0]);
#ifdef USE_SYSLOG
    syslog (LOG_INFO, "REHASH From %s\n", get_client_name (sptr, FALSE));
#endif
    return rehash (cptr, sptr, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
}

/*
   ** m_restart
   **
   ** parv[1] - password *OR* reason if no X:line
   ** parv[2] - reason for restart (optional & only if X:line exists)
   **
   ** The password is only valid if there is a matching X line in the
   ** config file. If it is not,  then it becomes the
 */
int m_restart (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char *pass = NULL;

    if (check_registered (sptr))
	return 0;

    if (!MyClient (sptr) || !OPCanRestart (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    if ((pass = find_restartpass())) {
	if (parc < 2) {
	    sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
			"RESTART");
	    return 0;
	}
	if (strcmp (pass, parv[1])) {
	    sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name, parv[0]);
	    return 0;
	}
	/* Hack to make the code after this if { } easier: we assign the comment to the
	 * first param, as if we had not had an X:line. We do not need the password
	 * now anyways. Accordingly we decrement parc ;)    -- NikB
	 */
	parv[1] = parv[2];
	parc--;
    }
#ifdef USE_SYSLOG
    syslog (LOG_WARNING, "Server RESTART by %s - %s\n",
	    get_client_name (sptr, FALSE),
	    (parc > 1 ? parv[1] : "No reason"));
#endif
    sendto_ops ("Server is Restarting by request of %s", parv[0]);
    server_reboot ((parv[1] ? parv[1] : "No Reason"));
    return 0;
}

/*
   ** m_trace
   **   parv[0] = sender prefix
   **   parv[1] = servername
 */
int m_trace (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int i;
    aClient *acptr;
    aClass *cltmp;
    char *tname;
    int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
    int cnt = 0, wilds, dow;
    time_t now;

    if (check_registered (sptr))
	return 0;

    if (parc > 2)
	if (hunt_server (cptr, sptr, ":%s TRACE %s :%s", 2, parc, parv))
	    return 0;

    if (parc > 1)
	tname = parv[1];
    else
	tname = me.name;

    if (!IsOper (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    switch (hunt_server (cptr, sptr, ":%s TRACE :%s", 1, parc, parv)) {
    case HUNTED_PASS:		  /* note: gets here only if parv[1] exists */
	{
	    aClient *ac2ptr;

	    ac2ptr = next_client (client, tname);
	    sendto_one (sptr, rpl_str (RPL_TRACELINK), me.name, parv[0],
			version, debugmode, tname, ac2ptr->from->name);
	    return 0;
	}
    case HUNTED_ISME:
	break;
    default:
	return 0;
    }

    doall = (parv[1] && (parc > 1)) ? !match (tname, me.name) : TRUE;
    wilds = !parv[1] || index (tname, '*') || index (tname, '?');
    dow = wilds || doall;

    for (i = 0; i < MAXCONNECTIONS; i++)
	link_s[i] = 0, link_u[i] = 0;

    if (doall)
	for (acptr = client; acptr; acptr = acptr->next)
#ifdef	SHOW_INVISIBLE_LUSERS

	{
	    if (IsPerson (acptr) && (!IsHidden (acptr) || IsOper (sptr)))
		link_u[acptr->from->fd]++;
#else
	{
	    if (IsPerson (acptr) &&
		((!IsHidden (acptr) && !IsInvisible (acptr))
		 || IsOper (sptr)))
		link_u[acptr->from->fd]++;
#endif

	    else if (IsServer (acptr))
		link_s[acptr->from->fd]++;
	}
    /* report all direct connections */

    now = time (NULL);
    for (i = 0; i <= highest_fd; i++) {
	char *name;
	int class;

	if (!(acptr = local[i]))  /* Local Connection? */
	    continue;
/* More bits of code to allow oers to see all users on remote traces
 *        if (IsInvisible(acptr) && dow &&
 *      if (dow &&
 *          !(MyConnect(sptr) && IsOper(sptr)) && */
	if (!IsOper (sptr) && !IsAnOper (acptr) && (acptr != sptr))
	    continue;
	if (!doall && wilds && match (tname, acptr->name))
	    continue;
	if (!dow && mycmp (tname, acptr->name))
	    continue;
	name = get_client_name (acptr, FALSE);
	class = get_client_class (acptr);

	switch (acptr->status) {
	case STAT_CONNECTING:
	    sendto_one (sptr, rpl_str (RPL_TRACECONNECTING), me.name,
			parv[0], class, name);
	    cnt++;
	    break;
	case STAT_HANDSHAKE:
	    sendto_one (sptr, rpl_str (RPL_TRACEHANDSHAKE), me.name,
			parv[0], class, name);
	    cnt++;
	    break;
	case STAT_ME:
	    break;
	case STAT_UNKNOWN:
	    sendto_one (sptr, rpl_str (RPL_TRACEUNKNOWN),
			me.name, parv[0], class, name);
	    cnt++;
	    break;
	case STAT_CLIENT:
	    /* Only opers see users if there is a wildcard
	     * but anyone can see all the opers.
	     */
/*          if (IsOper(sptr) &&
 * Allow opers to see invisible users on a remote trace or wildcard
 * search  ... sure as hell  helps to find clonebots.  --Russell
 *              (MyClient(sptr) || !(dow && IsInvisible(acptr)))
 *                           || !dow || IsAnOper(acptr)) */
	    if (IsOper (sptr) || (IsAnOper (acptr) && !IsInvisible (acptr))) {
		if (IsAnOper (acptr))
		    sendto_one (sptr,
				rpl_str (RPL_TRACEOPERATOR),
				me.name, parv[0], class, name,
				now - acptr->lasttime);
		else
		    sendto_one (sptr, rpl_str (RPL_TRACEUSER),
				me.name, parv[0], class, name,
				now - acptr->lasttime);
		cnt++;
	    }
	    break;
	case STAT_SERVER:
	    if (acptr->serv->user)
		sendto_one (sptr, rpl_str (RPL_TRACESERVER),
			    me.name, parv[0], class, link_s[i],
			    link_u[i], name, acptr->serv->by,
			    acptr->serv->user->username,
			    acptr->serv->user->host, now - acptr->lasttime);
	    else
		sendto_one (sptr, rpl_str (RPL_TRACESERVER),
			    me.name, parv[0], class, link_s[i],
			    link_u[i], name, *(acptr->serv->by) ?
			    acptr->serv->by : "*", "*", me.name,
			    now - acptr->lasttime);
	    cnt++;
	    break;
	case STAT_SERVICE:
	    sendto_one (sptr, rpl_str (RPL_TRACESERVICE),
			me.name, parv[0], class, name);
	    cnt++;
	    break;
	case STAT_LOG:
	    sendto_one (sptr, rpl_str (RPL_TRACELOG), me.name,
			parv[0], LOGFILE, acptr->port);
	    cnt++;
	    break;
	default:		  /* ...we actually shouldn't come here... --msa */
	    sendto_one (sptr, rpl_str (RPL_TRACENEWTYPE), me.name, parv[0],
			name);
	    cnt++;
	    break;
	}
    }
    /*
     * Add these lines to summarize the above which can get rather long
     * and messy when done remotely - Avalon
     */
    if (!IsAnOper (sptr) || !cnt) {
	if (cnt)
	    return 0;
	/* let the user have some idea that its at the end of the
	 * trace
	 */
	sendto_one (sptr, rpl_str (RPL_TRACESERVER),
		    me.name, parv[0], 0, link_s[me.fd],
		    link_u[me.fd], me.name, "*", "*", me.name);
	return 0;
    }
    for (cltmp = FirstClass (); doall && cltmp; cltmp = NextClass (cltmp))
	if (Links (cltmp) > 0)
	    sendto_one (sptr, rpl_str (RPL_TRACECLASS), me.name,
			parv[0], Class (cltmp), Links (cltmp));
    return 0;
}

/*
   ** m_motd
   **   parv[0] = sender prefix
   **   parv[1] = servername
 */
int m_motd (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int fd, nr;
    char line[80];
    char *tmp;
    struct stat sb;
    struct tm *tm;


    if (hunt_server (cptr, sptr, ":%s MOTD :%s", 1, parc, parv) !=
	HUNTED_ISME)
	return 0;
    /*
     * stop NFS hangs...most systems should be able to open a file in
     * 3 seconds. -avalon (courtesy of wumpus)
     */
    fd = open(MOTD, O_RDONLY);
    if (fd == -1) {
	sendto_one (sptr, err_str (ERR_NOMOTD), me.name, parv[0]);
	return 0;
    }
    (void) fstat (fd, &sb);
    sendto_one (sptr, rpl_str (RPL_MOTDSTART), me.name, parv[0], me.name);
    tm = localtime (&sb.st_mtime);
    sendto_one (sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_MOTD,
		parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
		tm->tm_hour, tm->tm_min);
    (void) dgets (-1, NULL, 0);	  /* make sure buffer is at empty pos */
    while ((nr = dgets (fd, line, sizeof (line) - 1)) > 0) {
	line[nr] = '\0';
	if ((tmp = (char *) index (line, '\n')))
	    *tmp = '\0';
	if ((tmp = (char *) index (line, '\r')))
	    *tmp = '\0';
	sendto_one (sptr, rpl_str (RPL_MOTD), me.name, parv[0], line);
    }
    (void) dgets (-1, NULL, 0);	  /* make sure buffer is at empty pos */
    sendto_one (sptr, rpl_str (RPL_ENDOFMOTD), me.name, parv[0]);
    (void) close (fd);
    return 0;
}

/*
   ** m_opermotd (added by GZ 12/04/99)
   **   parv[0] = sender prefix
   **   parv[1] = servername
 */
int m_opermotd (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int fd, nr;
    char line[80];
    char *tmp;
    struct stat sb;

    if (!MyClient (sptr) || !IsAnOper (sptr))
	return 0;
    /*
     * stop NFS hangs...most systems should be able to open a file in
     * 3 seconds. -avalon (courtesy of wumpus)
     */
    fd = open (OMOTD, O_RDONLY);
    if (fd == -1) {
	return 0;
    }
    (void) fstat (fd, &sb);
    sendto_one (sptr, rpl_str (RPL_OMOTDSTART), me.name, parv[0], me.name);
/*  tm = localtime(&sb.st_mtime);
   sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_OMOTD,
   parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
   tm->tm_hour, tm->tm_min);
 */
    (void) dgets (-1, NULL, 0);	  /* make sure buffer is at empty pos */
    while ((nr = dgets (fd, line, sizeof (line) - 1)) > 0) {
	line[nr] = '\0';
	if ((tmp = (char *) index (line, '\n')))
	    *tmp = '\0';
	if ((tmp = (char *) index (line, '\r')))
	    *tmp = '\0';
	sendto_one (sptr, rpl_str (RPL_OMOTD), me.name, parv[0], line);
    }
    (void) dgets (-1, NULL, 0);	  /* make sure buffer is at empty pos */
    sendto_one (sptr, rpl_str (RPL_ENDOFOMOTD), me.name, parv[0]);
    (void) close (fd);
    return 0;
}

/*
   ** m_close - added by Darren Reed Jul 13 1992.
 */
int m_close (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;
    int i;
    int closed = 0;

    if (check_registered (sptr))
	return 0;

    if (!MyOper (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    for (i = highest_fd; i; i--) {
	if (!(acptr = local[i]))
	    continue;
	if (!IsUnknown (acptr) && !IsConnecting (acptr)
	    && !IsHandshake (acptr))
	    continue;
	sendto_one (sptr, rpl_str (RPL_CLOSING), me.name, parv[0],
		    get_client_name (acptr, TRUE), acptr->status);
	(void) exit_client (acptr, acptr, acptr, "Oper Closing");
	closed++;
    }
    sendto_one (sptr, rpl_str (RPL_CLOSEEND), me.name, parv[0], closed);
    sendto_ops ("%s!%s@%s closed %d unknown connections", sptr->name,
		sptr->user->username, sptr->user->host, closed);
    return 0;
}

/* m_die, this terminates the server, and it intentionally does not
 * have a reason. If you use it you should first do a GLOBOPS and
 * then a server notice to let everyone know what is going down...
 */
int m_die (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;
    int i;
    char *pass = NULL;

    if (check_registered (sptr))
	return 0;

    if (!MyClient (sptr) || !OPCanDie (sptr)) {
	sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
	return 0;
    }
    if ((pass = find_diepass ())) {	/* See if we have and DIE/RESTART password */
	if (parc < 2) {		  /* And if so, require a password :) */
	    sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
			"DIE");
	    return 0;
	}
	if (strcmp (pass, parv[1])) {
	    sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name, parv[0]);
	    return 0;
	}
    }
    /* Let the +s know what is going on */
    sendto_ops ("Server Terminating by request of %s", parv[0]);

    for (i = 0; i <= highest_fd; i++) {
	if (!(acptr = local[i]))
	    continue;
	if (IsClient (acptr))
	    sendto_one (acptr,
			":%s NOTICE %s :Server Terminating. %s",
			me.name, acptr->name, get_client_name (sptr, TRUE));
	else if (IsServer (acptr))
	    sendto_one (acptr, ":%s ERROR :Terminated by %s",
			me.name, get_client_name (sptr, TRUE));
    }
    (void) s_die ();
    return 0;
}


char *militime (char *sec, char *usec)
{
    struct timeval tv;
    static char timebuf[18];

    gettimeofday (&tv, NULL);

    if (sec && usec)
#if defined(__sun__) || defined(__bsdi__)
	sprintf (timebuf, "%ld",
		 (tv.tv_sec - atoi (sec)) * 1000 + (tv.tv_usec -
						    atoi (usec)) / 1000);
#else
	sprintf (timebuf, "%ld",
		 (tv.tv_sec - atoi (sec)) * 1000 + (tv.tv_usec -
						    atoi (usec)) / 1000);
#endif
    else
#if defined(__sun__) || defined(__bsdi__)
	sprintf (timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
#else
	sprintf (timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
#endif
    return timebuf;
}

/*
 *  m_rping  -- by Run
 *
 *    parv[0] = sender prefix
 *    parv[1] = pinged server
 *    parv[2] = from person: start server ; from server: sender
 *    parv[3] = start time in s  ;from person: Optional remark
 *    parv[4] = start time in us
 */
int m_rping (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;

    if (!IsPrivileged (sptr))
	return 0;

    if (parc < (IsAnOper (sptr) ? (MyConnect (sptr) ? 2 : 3) : 6)) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name,
		    parv[0], "RPING");
	return 0;
    }
    if (MyClient (sptr)) {
	if (parc == 2)
	    parv[parc++] = me.name;
	else if (!(acptr = find_server_wildcard (parv[2])))
	    /* Should be replacd with a WILDCARD search routine for servers! */
	    /* Done. -GZ */

	{
	    parv[3] = parv[2];
	    parv[2] = me.name;
	    parc++;
	}
	else
	    parv[2] = acptr->name;

	if (parc == 3)
	    parv[parc++] = "<No client start time>";
    }
    if (IsAnOper (sptr)) {
	if (hunt_server (cptr, sptr, ":%s RPING %s %s :%s", 2, parc, parv) !=
	    HUNTED_ISME)
	    return 0;
	if (!(acptr = find_server_wildcard (parv[1])) || !IsServer (acptr))
	    /* Should be replaced with a WILDCARD search routine for servers! */
	    /* Done. -GZ */
	{
	    sendto_one (sptr, err_str (ERR_NOSUCHSERVER), me.name,
			parv[0], parv[1]);
	    return 0;
	}
	sendto_one (acptr, ":%s RPING %s %s %s :%s",
		    me.name, acptr->name, sptr->name, militime (NULL, NULL),
		    parv[3]);
    }
    else {
	if (hunt_server
	    (cptr, sptr, ":%s RPING %s %s %s %s :%s", 1, parc, parv)
	    != HUNTED_ISME)
	    return 0;
	sendto_one (cptr, ":%s RPONG %s %s %s %s :%s", me.name, parv[0],
		    parv[2], parv[3], parv[4], parv[5]);
    }

    return 0;
}

/*
 *  m_rpong  -- by Run too :)
 *
 *    parv[0] = sender prefix
 *    parv[1] = from pinged server: start server; from start server: sender
 *    parv[2] = from pinged server: sender; from start server: pinged server
 *    parv[3] = pingtime in ms
 *    parv[4] = client info (for instance start time)
 */
int m_rpong (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;

    if (!IsServer (sptr))
	return 0;

    if (parc < 5) {
	sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
		    "RPING");
	return 0;
    }
    if (!(acptr = find_client (parv[1], (aClient *) NULL)))
	return 0;

    if (!IsMe (acptr)) {
	if (IsServer (acptr) && parc > 5) {
	    sendto_one (acptr, ":%s RPONG %s %s %s %s :%s",
			parv[0], parv[1], parv[2], parv[3], parv[4], parv[5]);
	    return 0;
	}
    }
    else {
	parv[1] = parv[2];
	parv[2] = sptr->name;
	parv[0] = me.name;
	parv[3] = militime (parv[3], parv[4]);
	parv[4] = parv[5];
	if (!(acptr = find_person (parv[1], (aClient *) NULL)))
	    return 0;		  /* No bouncing between servers ! */
    }

    sendto_one (acptr, ":%s RPONG %s %s %s :%s", parv[0], parv[1], parv[2],
		parv[3], parv[4]);
    return 0;
}

/*
 * dump_map (used by m_map)
 */
void dump_map(aClient *cptr, aClient *server, char *mask, int prompt_length,
	  int length)
{
    static char prompt[64];
    char *p = &prompt[prompt_length];
    int cnt = 0, local = 0;
    aClient *acptr;

    *p = '\0';

    if (prompt_length > 60)
	sendto_one (cptr, rpl_str (RPL_MAPMORE), me.name, cptr->name, prompt,
		    server->name);
    else {
	for (acptr = client; acptr; acptr = acptr->next) {
	    if (IsPerson (acptr)) {
		++cnt;
		if (!strcmp (acptr->user->server, server->name))
		    ++local;
	    }
	}

	sendto_one (cptr, rpl_str (RPL_MAP), me.name, cptr->name, prompt,
		    length, server->name, local, (local * 100) / cnt);
	cnt = 0;
    }

    if (prompt_length > 0) {
	p[-1] = ' ';
	if (p[-2] == '`')
	    p[-2] = ' ';
    }
    if (prompt_length > 60)
	return;

    strcpy (p, "|-");


    for (acptr = client; acptr; acptr = acptr->next) {
	if (!IsServer (acptr) || strcmp (acptr->serv->up, server->name))
	    continue;

	if (match (mask, acptr->name))
	    acptr->flags &= ~FLAGS_MAP;
	else {
	    acptr->flags |= FLAGS_MAP;
	    cnt++;
	}
    }

    for (acptr = client; acptr; acptr = acptr->next) {
	if (!(acptr->flags & FLAGS_MAP) ||	/* != */
	    !IsServer (acptr) || strcmp (acptr->serv->up, server->name))
	    continue;
	if (--cnt == 0)
	    *p = '`';
	dump_map (cptr, acptr, mask, prompt_length + 2, length - 2);
    }

    if (prompt_length > 0)
	p[-1] = '-';
}

/*
   ** m_map
   **
   **      parv[0] = sender prefix
   **      parv[1] = server mask
   * */
int m_map (aClient * cptr, aClient * sptr, int parc, char *parv[])
{

    aClient *acptr;
    int longest = strlen (me.name);

    if (!IsOper(cptr)) {
	    sendto_one ( cptr, "Map is not allowed on this server.");
	    return 0;
    }

    if (check_registered (sptr))
	return 0;

    if (parc < 2)
	parv[1] = "*";

    for (acptr = client; acptr; acptr = acptr->next)
	if (IsServer (acptr)
	    && (strlen (acptr->name) + acptr->hopcount * 2) > longest)
	    longest = strlen (acptr->name) + acptr->hopcount * 2;

    if (longest > 60)
	longest = 60;
    longest += 2;
    dump_map (sptr, &me, parv[1], 0, longest);
    sendto_one (sptr, rpl_str (RPL_MAPEND), me.name, parv[0]);

    return 0;
}

int find_services(void)
{

    aClient *cptr;

    if ((cptr = find_server (SERVICES_NAME, (aClient *) NULL)))
	return 1;

    return 0;

}

/*
 *	m_snick added by GZ November '99
 *
 *	parv[0] = source
 *	parv[1] = nick
 *	parv[2] = hopcount
 *	parv[3] = lastnick
 *	parv[4] = username
 * 	parv[5] = hostname
 * 	parv[6] = server
 *	parv[7] = services stamp
 * 	parv[8] = modes
 * 	parv[9] = real name
 */

int m_snick(aClient * cptr, aClient * sptr, int parc, char *parv[])
{
    aClient *acptr;

    if (!IsServer (sptr) || parc < 9)
	return 1;

    /* New KILL in case something messes up */

    if (do_snick (cptr, sptr, parc, parv)) {
	sendto_one (cptr, ":%s KILL %s :%s (Synchronizing error. (SNICK))",
		    me.name, parv[1], me.name);
	return 1;
    }

    if ((acptr = find_client (parv[1], NULL))) {

	sendto_SNICK_butone (sptr, "SNICK %s %d %d %s %s %s %lu %s :%s",
			     acptr->name, acptr->hopcount + 1,
			     acptr->lastnick, acptr->user->username,
			     acptr->user->host, acptr->user->server,
			     acptr->user->servicestamp, parv[8], acptr->info);
	return 0;
    }

    sendto_serv_butone (&me, ":%s GLOBOPS :ERROR with SNICK for %s", me.name,
			parv[1]);

    return 1;
}

/*
 * do_snick - added November 99 by GZ
 *
 * This will make a client-structure out of the parameters
 * that SNICK gives us. It's basicly a stripped down merge
 * of m_nick, m_user and register_user.
 *
 *	parv[0] = source
 *	parv[1] = nick
 *	parv[2] = hopcount
 *	parv[3] = lastnick
 *	parv[4] = username
 * 	parv[5] = hostname
 * 	parv[6] = server
 *	parv[7] = services stamp
 * 	parv[8] = modes
 * 	parv[9] = real name
 *
 */

int do_snick(aClient * cptr, aClient * sptr, int parc, char *parv[])
{

    aClient *acptr, *serv = NULL;
    anUser *user;
    aConfItem *aconf;
    aSqlineItem *asqline;
    int keep, sameguy;
    int ourts, theirts;


    if (!(serv = find_server (parv[6], NULL)) || serv->from != cptr->from) {
	return 1;
    }

    if ((acptr = find_client (parv[1], NULL))) {
	keep = sameguy = 0;
	ourts = acptr->lastnick;
	theirts = atoi (parv[3]);
	if (ourts < theirts)
	    keep = 1;
	sameguy = !(strcmp (acptr->user->host, parv[5]));

	if (sameguy)
	    keep = !keep;

	if (!keep) {
	    if (sameguy) {
		sendto_serv_butone (cptr,
				    ":%s KILL %s :%s (Nick collision (old nick killed))",
				    me.name, acptr->name, me.name);
		acptr->flags |= FLAGS_KILLED;
		(void) exit_client (NULL, acptr, &me,
				    "Nick collision (old nick killed)");
	    }
	    else {
		sendto_serv_butone (cptr,
				    ":%s KILL %s :%s (Nick collision (new nick killed))",
				    me.name, acptr->name, me.name);
		acptr->flags |= FLAGS_KILLED;
		(void) exit_client (NULL, acptr, &me,
				    "Nick collision (new nick killed)");
	    }
	}
    }

    sptr = make_client (cptr, serv);
    add_client_to_list (sptr);

    strncpyzt (sptr->name, parv[1], sizeof (sptr->name));

    if (parc > 2)
	sptr->hopcount = atoi (parv[2]);
    if (parc > 3)
	sptr->lastnick = atoi (parv[3]);
    else
	sptr->lastnick = time (NULL);

    (void) add_to_client_hash_table (parv[1], sptr);
    user = make_user (sptr);

    strncpyzt (user->server, parv[6], sizeof (user->server));
    strncpyzt (user->host, parv[5], sizeof (user->host));
    user->servicestamp = atol (parv[7]);
    strncpyzt (sptr->info, parv[9], sizeof (sptr->info));
    strncpyzt (user->username, parv[4], USERLEN + 1);

    SetClient (sptr);

    if ((aconf = find_conf_name (parv[1], CONF_QUARANTINED_NICK))
	|| (asqline = find_sqline_match (parv[1]))) {
// fixme
//              char *qrsn = (aconf) ? aconf->passwd : asqline->reason;
	char *qtyp = (aconf) ? "Q:Lined" : "SQLined";

	if (!find_conf_host (cptr->confs, sptr->user->server, CONF_UWORLD))
	    sendto_realops ("%s nick %s from %s@%s on %s.", qtyp, parv[1],
			    sptr->user->username, sptr->user->host,
			    sptr->user->server);
    }

    /* Check for services */

    if (find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)
	|| (sptr->user
	    && find_conf_host (cptr->confs, sptr->user->server, CONF_UWORLD)))
	sptr->flags |= FLAGS_ULINE;

    /* Change modes for the user here */

    mode_snick (sptr, parv[8]);

    /* If user is masked - then update mask accordingly here */

    calc_mask(sptr) ;

    /* Do a WATCH notify - user should be masked by now, so it's safe */

    hash_check_notify (sptr, RPL_LOGON);

    return 0;
}

/*
 * mode_snick - sets modes given by SNICK
 * added by GZ Nov '99
 */

int mode_snick(aClient * acptr, char *modes)
{
    char *p;
    int what;

    p = modes;

    what = MODE_ADD;

    for (p = modes; *p; p++) {
	switch (*p) {
	case '+':
	    what = MODE_ADD;
	    break;
	case '-':
	    what = MODE_DEL;
	    break;
	case 'n':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_SROOT;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_SROOT;
	    }
	    break;
	case 'o':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_OPER;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_OPER;
	    }
	    break;
	case 'i':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_INVISIBLE;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_INVISIBLE;
	    }
	    break;
	case 'g':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_FAILOP;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_FAILOP;
	    }
	    break;
	case 'k':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_KILLS;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_KILLS;
	    }
	    break;
	case 'a':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_SADMIN;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_SADMIN;
	    }
	    break;
	case 'N':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_NETADMIN;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_NETADMIN;
	    }
	    break;
	case 'M':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_NETMANAG;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_NETMANAG;
	    }
	    break;
	case 'A':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_ADMIN;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_ADMIN;
	    }
	    break;
	case 'c':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_CLIENT;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_CLIENT;
	    }
	    break;
	    break;
	case 'F':
		if (what == MODE_ADD) {
		acptr->umodes |= UMODE_RCLIENT;
		}
		else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_RCLIENT;
		}
		break;
		break;
	case 'x':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_HIDE;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_HIDE;
	    }
	    break;
	case 'y':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_WHOIS;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_WHOIS;
	    }
	    break;
	case 'I':
	    if (what == MODE_ADD) {
		acptr->umodes |= UMODE_IDENTIFY;
	    }
	    else if (what == MODE_DEL) {
		acptr->umodes &= ~UMODE_IDENTIFY;
	    }
	    break;
	}
    }
    return 0;

}
